import jieba
import re
import math
from openpyxl import load_workbook


negative_file = '否定词.txt'
adv_file = '程度副词.xlsx'
emotional_file = '情感词汇本体.xlsx'
test_file = '测试文章.txt'

alpha = 0.75   # 不确定的情感值极性前后判断因素的比例，默认为0.75

'''
判断情感词极性，传入参数：情感词，情感字典
判断依据：情感分为0， 1， -1， 3，分别表示：中性，积极，消极，褒(积)贬(消)不一四种
1、若一个情感词有两种相同的极性，例如都是1，或者-1,0，那么情感值由第一个[强度，极性]确定
2、若一个情感词有两种相同的极性且都是3，那么该情感值的极性由该情感词前0-4个的情感值极性*0.75(可以修改前后比重), 后0-4个情感值极性*0.25的和共同确定，计算只包括0,1,-1。
    最后结果根据离-1，0,1这三个数的绝对距离确定
3、若一个情感词前后极性不同，那么该情感值的极性同上。
4、若只有一个情感词，则直接返回该数值即可

'''
def anaysisPolarity(word, dict_of_emtion):
    str1 = dict_of_emtion[word][0][0]  # 第一个强度
    plo1 = dict_of_emtion[word][0][1]  # 第一个极性
    if len(dict_of_emtion[word]) > 1:   # 若有两个极性
        str2 = dict_of_emtion[word][1][0]
        plo2 = dict_of_emtion[word][1][1]
        if plo1 == plo2 and plo1 in [-1, 0, 1]:  # 判断依据1
            return [[str1, plo1]]            #  返回第一个强度，极性
        elif (plo1 == plo2 and plo1 == 3) or (plo1 != plo2):  # 判断依据2,3
            return [[str1, plo1], [str2, plo2]]         # 两个都返回
    else:
        return [[str1, plo1]]

'''传入参数：文段，情感字典，副词字典，否定词列表'''

def analysisWords(words, dict_of_emtion, dict_of_adv, list_of_negative, par_W):
    for word in words:
        if word in dict_of_emtion.keys():  # 如果这个词在情感词中，则进行分析
            w3 = 1  # 默认没有否定词
            w4 = 1  # 默认副词为没有，也就是弱，为1
            w1w2 = anaysisPolarity(word, dict_of_emtion)  # 判断极性
            for num in range(1, words.index(word)):
                index = words.index(word) - num
                index_w = words[index]  # 当前下标表示的词语
                if index_w == '，':
                    break
                else:
                    if index_w in list_of_negative:  # 如果在否定词列表中
                        w3 *= -1  # 找到了否定词，置为-1
                    if index_w in dict_of_adv.keys():
                        w4 = dict_of_adv[index_w]  # 副词
            try:
                par_W.append([w1w2, w3, w4])
            except Exception as e:
                print("错误:", e)

def main():
    '''读取情感词汇到字典'''
    wb = load_workbook(emotional_file)
    ws = wb[wb.sheetnames[0]]  # 读取第一个sheet
    dict_of_emtion = {}
    for i in range(2, ws.max_row):
        word = ws['A' + str(i)].value
        strength = ws['F' + str(i)].value  # 一个强度
        polarity = ws['G' + str(i)].value  # 一个极性
        if polarity == 2:
            polarity = -1
        assist = ws['H' + str(i)].value  # 辅助情感分类
        if word not in dict_of_emtion.keys():
            dict_of_emtion[word] = list([[strength, polarity]])
        else:
            dict_of_emtion[word].append([strength, polarity])  # 添加二义性的感情词
        if assist != None:
            str2 = ws['I' + str(i)].value
            pola2 = ws['J' + str(i)].value
            if pola2 == 2:
                pola2 = -1
            dict_of_emtion[word].append([str2, pola2])

    '''读取程度副词副字典'''
    dict_of_adv = {}
    wb = load_workbook(adv_file)
    ws = wb[wb.sheetnames[0]]
    for i in range(2, ws.max_row):
        dict_of_adv[ws['A' + str(i)].value] = ws['B' + str(i)].value
    '''读取否定词列表'''
    list_of_negative = []
    with open(negative_file, "r", encoding='utf-8') as f:
        temps = f.readlines()
    for temp in temps:
        list_of_negative.append(temp.replace("\n", ""))

    para_W = []  # W1*W2*W3*W4参数的值
    with open(test_file, 'r', encoding="utf-8") as f:
        txt = f.readlines()
    for info in txt:
        article = re.findall(r'[^。！？\s]+', info)  # 一句一句分析
        if len(article) != 0:
            for par in article:
                words = jieba.lcut(par)  # 一句一句分词
                analysisWords(words, dict_of_emtion, dict_of_adv, list_of_negative, para_W)
    new_para_W = {}
    index = 1  # 情感参数的数量
    for x in para_W:  # 将只有一个情感极值的列表合并
        if len(x[0]) == 1:
            w = x[0][0][0] * x[0][0][1]
            for numx in x[1:]:
                w *= numx
            new_para_W[index] = w
        else:
            new_para_W[index] = x
        index += 1
    for i in range(1, len(new_para_W) + 1):
        if type(new_para_W[i]) == list:  # 如果该情感值是未计算的
            temp_result = 0
            k = i-1 if i-1 != 0 else i
            index = 1
            while index <= 4:  #  计算0-4个的值
                if type(new_para_W[k]) != list:
                    temp_result += new_para_W[k] * alpha   # 当前值乘以alaph
                else:
                    temp_result += new_para_W[k][0][0][1] * alpha  #如果没有，则默认为第一个极性
                k -= 1
                index += 1
                if k <= 0:
                    break
            k = i + 1 if i+1 < len(new_para_W) else i  #  计算后四个的值
            index = 1
            while index <= 4:
                if type(new_para_W[k]) != list and new_para_W[k] != 3:  # 只考虑后面
                    temp_result += new_para_W[k] * (1-alpha)
                k += 1
                index += 1
                if k > len(new_para_W):  # 如果超出了最长长度，后面则不考虑计算
                    break
            w2 = distanceOfNum(temp_result, new_para_W[i][0][0][1], new_para_W[i][0][1][1])  # 求出w2
            dict_of_str_plo = {}  # 存放极值---强度的字典
            str1 = new_para_W[i][0][0][0]
            plo1 = w2 if new_para_W[i][0][0][1] == 3 else new_para_W[i][0][0][1]  # 将褒贬不一的置为求出的局部感情极值
            str2 = new_para_W[i][0][1][0]
            plo2 = w2 if new_para_W[i][0][1][1] == 3 else new_para_W[i][0][1][1]
            dict_of_str_plo[plo2] = str2
            dict_of_str_plo[plo1] = str1
            if w2 == 0:
                new_para_W[i] = 0
            else:
                try:
                    new_para_W[i] = dict_of_str_plo[w2] * new_para_W[i][1] * new_para_W[i][2]
                except Exception as e:
                    print("错误：", e)
    molecular = 0   # 分子
    denominator = 0  # 分母
    for value in new_para_W.values():
        molecular += value
        denominator += math.fabs(value)
    if denominator == 0:
        print("情感倾向 = 0.00")
    else:
        print("情感倾向 = %.4f" % (molecular / denominator))

def distanceOfNum(result, x1, x2):
    num1 = math.fabs(result - x1)
    num2 = math.fabs(result - x2)
    if num1 > num2:
        return x2
    else:
        return x1


if __name__ == '__main__':
    main()
